package naivebayes;

import java.text.DecimalFormat;
import java.util.AbstractCollection;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Iterator;

/**
 *
 * @author Rodrigo
 */
public class ClasificadorBayesiano {

	private AbstractCollection<Ejemplo> ejemplos;
	private AbstractList<AbstractList<Character>> tipoAtributos;
	static private char targetPositivo = 'e';
	static private char targetNegativo = 'p';
	static private double alfaPositivo = 1.0;
	static private double alfaNegativo = 20.0;

	ClasificadorBayesiano(AbstractCollection<Ejemplo> ejemplos, AbstractList<AbstractList<Character>> tipoAtributos) {
		this.ejemplos = ejemplos;
		this.tipoAtributos = tipoAtributos;
	}

	char clasificar(Ejemplo nuevoej) {
		char mejorClasificador = tipoAtributos.get(0).get(0);
		double mejorProbabilidad = 0;
		Iterator it = tipoAtributos.get(0).iterator();
		while (it.hasNext()) {
			Character clase = (Character) it.next();
			double probEj = calcularProbabilidadClase(clase);
			int max = nuevoej.getMax();
			for (int i = 1; i < max; i++) {
				probEj *= calcProbEj(clase, i, nuevoej.getVal(i));
			}
			if (probEj > mejorProbabilidad) {
				mejorProbabilidad = probEj;
				mejorClasificador = clase;
			}
		}
		return mejorClasificador;
	}

	double calcularProbabilidadClase(char clase) {
		Iterator it = ejemplos.iterator();
		int cantClase = 0;
		int cantTotal = 0;
		while (it.hasNext()) {
			Ejemplo ej = (Ejemplo) it.next();
			if (ej.getVal(0) == clase) {
				cantClase++;
			}
			cantTotal++;
		}
		double alfa = alfaNegativo;
		if(clase == targetPositivo){
			alfa = alfaPositivo;
		}
		return alfa*((((double) cantClase) + 1) / (((double) cantTotal) + tipoAtributos.get(0).size()));
	}

	double calcProbEj(char clase, int numAtrib, char atrib) {
		Iterator itEj = ejemplos.iterator();
		int cantAtrib = 0;
		int cantClase = 0;
		while (itEj.hasNext()) {
			Ejemplo ej = (Ejemplo) itEj.next();
			if (ej.getVal(0) == clase) {
				if (ej.getVal(numAtrib) == atrib) {
					cantAtrib++;
				}
				cantClase++;
			}
		}
		return (((double) cantAtrib) + 1) / (((double) cantClase) + tipoAtributos.get(numAtrib).size());
	}

	int[][] matrizConfusion(ArrayList ejemplos) {
		int[][] matriz = new int[2][2];
		matriz[0][0] = 0;
		matriz[0][1] = 0;
		matriz[1][0] = 0;
		matriz[1][1] = 0;
		Iterator it = ejemplos.iterator();
		while (it.hasNext()) {
			Ejemplo ej = (Ejemplo) it.next();
			if (ej.getVal(0) == targetPositivo) {
				if (this.clasificar(ej) == ej.getVal(0)) {
					matriz[0][0]++;
				}else{
					matriz[1][0]++;
				}
			} else {
				if (this.clasificar(ej) == ej.getVal(0)) {
					matriz[0][1]++;
				}else{
					matriz[1][1]++;
				}
			}

		}
		return matriz;
	}

	static void probar(AbstractCollection<Ejemplo> ejemplos, int verif,
			AbstractList<AbstractList<Character>> tipoAtributos) {
		long time1= System.nanoTime();

		int numInst = ejemplos.size();
		int subNumInst = ejemplos.size() / 10;
		AbstractCollection<Ejemplo> ejemplosCostruir = new ArrayList();
		AbstractCollection<Ejemplo> ejemplosVerif = new ArrayList();
		for (int i = (verif - 1) * subNumInst; i < verif * subNumInst; i++) {

			ejemplosVerif.add((Ejemplo) ((ArrayList) ejemplos).get(i));
		}
		for (int i = 0; i < (verif - 1) * subNumInst; i++) {
			ejemplosCostruir.add((Ejemplo) ((ArrayList) ejemplos).get(i));
		}

		for (int i = (verif - 1) * subNumInst + subNumInst; i < numInst; i++) {
			ejemplosCostruir.add((Ejemplo) ((ArrayList) ejemplos).get(i));
		}
		ClasificadorBayesiano bayes = new ClasificadorBayesiano(ejemplosCostruir, tipoAtributos);
		int[][] matriz = bayes.matrizConfusion((ArrayList) ejemplosVerif);
		Double error = (1.0/(double)subNumInst)*((double)matriz[1][0] + (double)matriz[1][1]);
		DecimalFormat twoDForm = new DecimalFormat("#.##");
		
		long time2= System.nanoTime();
		double timeSpent = ((double)(time2-time1))*(Math.pow(10.0, -9.0));


		System.out.println("Para test=" + verif + " la matriz de confusion es:");
		System.out.println("     | Comestible | Venenoso");
		System.out.println("Bien | "+matriz[0][0] + "	| "+matriz[0][1]);
		System.out.println("Mal  | "+matriz[1][0] + "	  | "+matriz[1][1]);
		System.out.println("Error: "+ twoDForm.format(error));
		System.out.println("Tiempo de ejecucion: "+ timeSpent);
		System.out.println("");
	}
}
